/**
 * 单总线介绍
 * 单总线（1-Wire BUS）是由Dallas公司开发的一种通用数据总线
 * 一根通信线：DQ
 * 异步(没有时钟线)、半双工
 * 单总线只需要一根通信线即可实现数据的双向传输，
 * 当采用寄生供电时，还可以省去设备的VDD线路，此时，供电加通信只需要DQ和GND两根线
 * 
 * 单总线电路规范
 * 设备的DQ均要配置成开漏输出模式
 * DQ添加一个上拉电阻，阻值一般为4.7KΩ左右
 * 若此总线的从机采取寄生供电，则主机还应配一个强上拉输出电路
 */
#include <REGX52.H>
#include <INTRINS.H>

//单总线接口, 视频是P3_7
sbit _1WIRE_DQ = P2^2;

/**
 * 初始化：主机将总线拉低至少480us，然后释放总线，等待15~60us后，
 * 存在的从机会拉低总线60~240us以响应主机，之后从机将释放总线
 * @return 0从机应答, 1没应答
 */
bit _1WIRE_Init() {
    unsigned char i;
    bit acknowledge;
    _1WIRE_DQ = 1;
    _1WIRE_DQ = 0;
     _nop_(); i = 230; while (--i); //Delay500us, @11.0592MHz
    _1WIRE_DQ = 1;
    _nop_(); i = 32; while (--i);   //Delay70us, @11.0592MHz
    acknowledge = _1WIRE_DQ;
     _nop_(); i = 230; while (--i); //Delay500us, @11.0592MHz
     return acknowledge;
}

/**
 * 发送一位：主机将总线拉低60~120us，然后释放总线，表示发送0；
 *          主机将总线拉低1~15us，然后释放总线，表示发送1。
 *          从机将在总线拉低30us后（典型值）读取电平，整个时间片应大于60us
 */
void _1WIRE_sendBit(bit Bit) {
    unsigned char i;
    _1WIRE_DQ = 0;
    i = 4; while (--i);             //Delay10us, @11.0592MHz
    _1WIRE_DQ = Bit;
    _nop_(); i = 22; while (--i);   //Delay50us, @11.0592MHz
    _1WIRE_DQ = 1;
}

/**
 * 接收一位：主机将总线拉低1~15us，然后释放总线，
 * 并在拉低后15us内读取总线电平（尽量贴近15us的末尾），
 * 读取为低电平则为接收0，读取为高电平则为接收1 ，整个时间片应大于60us
 * 然后从机释放总线
 * @return 接收的1bit
 */
bit _1WIRE_receiveBit() {
    unsigned char i;
    bit Bit;
    _1WIRE_DQ = 0;
    _nop_(); _nop_(); _nop_();      //4us, @11.0592MHz
    _nop_();                        //≈5us
    _1WIRE_DQ = 1;
    i = 3; while (--i);             //Delay7us, @11.0592MHz
    Bit = _1WIRE_DQ;
    _nop_(); i = 21; while (--i);   //Delay48us, @11.0592MHz
    return Bit;
}

/**
 * 发送一个字节：连续调用8次发送一位的时序，依次发送一个字节的8位（低位在前）
 */
void _1WIRE_sendByte(unsigned char ch) {
    unsigned char i;
    for (i = 0; i < 8; i++) {
        /**
         * char val = ch & 1<<i;
         * 如果val==0, 说明ch第i位是0, 传入的参数(bit类型)也是0
         * 如果val!=0, 说明ch第i位是1, 传入的参数(bit类型)也是1
         */
        _1WIRE_sendBit(ch & 1<<i);
    }
}

/**
 * 接收一个字节：连续调用8次接收一位的时序，依次接收一个字节的8位（低位在前）
 */
unsigned char _1WIRE_receiveByte() {
    //ch一定要初始化, 否则收到的结果很乱
    unsigned char ch = 0, i;
    for (i = 0; i < 8; i++) {
        if (_1WIRE_receiveBit()) ch |= 1<<i;
    }
    return ch;
}
